﻿using MgSoft.Excel;
using MgSoft.Import.Excel.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Autofac;
using System.Threading;
using MgSoft.Log;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace MgSoft.Import.Excel
{
    public abstract class ExcelTaskManager<TDto> : TaskManager, ITaskManagerValidators, IExcelTaskManager where TDto : ExcelDto, new()
    {
        /// <summary>
        /// 由于TaskManager是通过AutoFac扫描注入的，这个属性是用来判断是否需要显示该任务
        /// </summary>
        public virtual bool IsDisplay => true;
        /// <summary>
        /// 线程池最大线程数
        /// </summary>
        protected virtual int MaxThreaderCount { get; } = 40;
        /// <summary>
        /// 启用线程池
        /// </summary>
        protected virtual bool useThreadPoolFlag { get; } = true;
        protected static AutoResetEvent threadEndEvent = new AutoResetEvent(false);
        protected static List<ManualResetEvent> threadEndEventList = new List<ManualResetEvent>();

        protected static List<Task> taskList = new List<Task>();

        private readonly ILifetimeScope lifetimeScope;
        protected int checkBusiness_totalThreadNumber = 0;
        private object checkBusinessAction_checkThreadPoolIsEnd_lockObj = new object();
        protected int do_totalThreadNumber = 0;
        private static object do_totalThreadNumber_lockObj = new object();

        /// <summary>
        /// 表格转换为Dto列表
        /// </summary>
        public List<TDto> Dtos { get; set; }

        /// <summary>
        /// 实现ExcelTaskManager的类型名称，用于名称注入
        /// </summary>
        public override string Name => this.GetType().FullName;

        /*/// <summary>
        /// TaskManager中文标题
        /// </summary>
        public abstract string Title { get; }

        /// <summary>
        /// TaskManager的描述说明
        /// </summary>
        public abstract string Describe { get; }*/

        ///// <summary>
        ///// Excel表格文件路径
        ///// </summary>
        //protected string ExcelFilePath { get; private set; }

        /// <summary>
        /// 具体实现的ExcelTask
        /// </summary>
        protected abstract IExcelTask<TDto> ExcelTask { get; }

        /// <summary>
        /// Excel读取数据的Dao
        /// </summary>
        protected abstract IExcelDao ExcelDao { get; }

        /// <summary>
        /// 读取到的Excel
        /// </summary>
        public MgExcel MgExcel { get; private set; }

        protected FileExcelTaskTypeInfo FileExcelTaskTypeInfo { get; private set; }

        protected IMgLog log { get; private set; } = new NullMgLog();

        public virtual string ScanScheme { get; } = "";

        protected ExcelTaskManager(Autofac.ILifetimeScope lifetimeScope)
        {
            this.lifetimeScope = lifetimeScope;
            IMgLogger mgLogger;
            lifetimeScope.TryResolve<IMgLogger>(out mgLogger);
            if (mgLogger != null) log = mgLogger.CreateLog();
        }


        /// <summary>
        /// 初始化表格，并对数据进行校验
        /// </summary>
        /// <param name="aggregateExcelMessage"></param>
        public virtual void InitAndCheck(TaskManagerInfoArg taskManagerInfoArg)
        {
            AggregateExcelMessage aggregateExcelMessage = taskManagerInfoArg.AggregateExcelMessage;
            try
            {

                BeforeInit(taskManagerInfoArg);
                if (hasErrorOrException(aggregateExcelMessage)) return;

                initCheck(taskManagerInfoArg);
                if (hasErrorOrException(aggregateExcelMessage)) return;

                initMgExcel(taskManagerInfoArg);
                if (hasErrorOrException(aggregateExcelMessage)) return;

                AfterInit(taskManagerInfoArg);
                if (hasErrorOrException(aggregateExcelMessage)) return;

                CheckMgRow(taskManagerInfoArg);
                if (hasErrorOrException(aggregateExcelMessage)) return;

                Dtos = ConvertToDto(taskManagerInfoArg);
                if (hasErrorOrException(aggregateExcelMessage)) return;

                CheckDto(taskManagerInfoArg);
            }
            catch (AggregateErrorException aggregateErrorException)
            {
                //Excel表格校验失败
                taskManagerInfoArg.AggregateExcelMessage.AddRange(aggregateErrorException.ErrorMessages, ExcelMessageType.Exception, FileExcelTaskTypeInfo);
            }
            catch (MgException mgException)
            {
                log.Error(mgException.Message);
                taskManagerInfoArg.AggregateExcelMessage.Add(new ExcelMessage(mgException.Message, "", ExcelMessageType.Exception, FileExcelTaskTypeInfo));
            }
            catch (Exception exception)
            {
                log.Error(exception.Message + "\n" + exception.StackTrace);
                taskManagerInfoArg.AggregateExcelMessage.Add(exception.Message, exception.StackTrace, ExcelMessageType.Exception, FileExcelTaskTypeInfo);
            }
            /*finally
            {
                WriteErrorToExcel(aggregateExcelMessage);
            }*/
        }

        private bool hasErrorOrException(AggregateExcelMessage aggregateExcelMessage)
        {
            return aggregateExcelMessage.HasError() || aggregateExcelMessage.HasException();
        }

        /// <summary>
        /// 导入数据
        /// </summary>
        /// <param name="taskManagerInfoArg"></param>
        public virtual void Do(TaskManagerInfoArg taskManagerInfoArg)
        {
            if ((Dtos?.Count ?? 0) == 0) return;

            var processInfo = (FileExcelTaskTypeInfo as IProcessInfo);
            processInfo.SetProcessTotalRow(this.MgExcel.MgSheet.GetTotleRow());
            ThreadPool.SetMaxThreads(MaxThreaderCount, MaxThreaderCount);
            do_totalThreadNumber = Dtos.Count;

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            foreach (var dto in Dtos)
            {
                var taskManagerInfoArgAndTDto = new TaskManagerInfoArgAndTDto()
                {
                    taskManagerInfoArg = taskManagerInfoArg,
                    tDto = dto,
                    processInfo = processInfo
                };
                //ExcelTask.Do(dto, taskManagerInfoArg);
                //区分是否启用线程池
                if (useThreadPoolFlag)
                {
                    ThreadPool.QueueUserWorkItem(new WaitCallback(DoAction), taskManagerInfoArgAndTDto);
                }
                else
                {
                    DoAction(taskManagerInfoArgAndTDto);
                }

            }

            // 等待线程池执行完。
            if (useThreadPoolFlag)
            {
                if (Dtos.Count <= 0) threadEndEvent.Set();
                threadEndEvent.WaitOne();
            }

            processInfo.SetFinsh();
            AfterDo(taskManagerInfoArg);

            stopwatch.Stop();
            Debug.WriteLine("数据同步完成++++++++++++++++++++++++++++++++++++");
            Debug.WriteLine($"用时:{stopwatch.ElapsedMilliseconds}");
        }

        protected virtual void AfterDo(TaskManagerInfoArg taskManagerInfoArg)
        {
        }

        public virtual void DoAction(object obj)
        {
            var param = obj as TaskManagerInfoArgAndTDto;
            if (param == null)
            {
                return;
            }
            try
            {
                ExcelTask.Do(param.tDto, param.taskManagerInfoArg);
                // 测试用休眠
                //Thread.Sleep(1000);
            }
            catch (MgExcelException mgExcelException)
            {
                param.taskManagerInfoArg.AggregateExcelMessage.Add(param.tDto.Row.RowIndex, mgExcelException.ColumnIndex, mgExcelException.Message, "", ExcelMessageType.Exception, FileExcelTaskTypeInfo);
            }
            catch (MgException mgException)
            {
                param.taskManagerInfoArg.AggregateExcelMessage.Add(param.tDto.Row.RowIndex, 0, mgException.Message, "", ExcelMessageType.Exception, FileExcelTaskTypeInfo);
            }
            catch (Exception exception)
            {
                log.Error(exception.Message + "\n" + exception.StackTrace);
                param.taskManagerInfoArg.AggregateExcelMessage.Add(param.tDto.Row?.RowIndex, 0, exception.Message, exception.StackTrace, ExcelMessageType.Exception, FileExcelTaskTypeInfo);
            }
            finally
            {
                param.processInfo.SetProcessRow(getNextSetp(param.tDto));
                if (useThreadPoolFlag)
                {
                    lock (do_totalThreadNumber_lockObj)
                    {
                        //Debug.WriteLine($@"{param.manualResetEvent.Handle}");
                        //param.manualResetEvent.Set();
                        doAction_checkThreadPoolIsEnd();
                    }
                }
            }
        }

        private int getNextSetp(TDto dto)
        {
            int nextSetp = 1;
            IRowSetp rowSetp = dto as IRowSetp;
            if (rowSetp != null)
                nextSetp = rowSetp.RowSetp;
            else
                nextSetp = 1;
            return nextSetp;
        }

        protected void doAction_checkThreadPoolIsEnd()
        {
            do_totalThreadNumber--;
            //// 全局变量未同步数据减1
            //do_totalThreadNumber = do_totalThreadNumber - 1;
            if (do_totalThreadNumber <= 0)
            {
                threadEndEvent.Set();
            }
        }

        #region 可重载方法
        /// <summary>
        /// 自定义校验
        /// </summary>
        /// <param name="aggregateExcelMessage"></param>
        protected virtual void CheckDto(TaskManagerInfoArg taskManagerInfoArg)
        {
            ThreadPool.SetMaxThreads(MaxThreaderCount, MaxThreaderCount);
            checkBusiness_totalThreadNumber = Dtos.Count;
            var processInfo = (FileExcelTaskTypeInfo as IProcessInfo);

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            foreach (var dto in Dtos)
            {
                var taskManagerInfoArgAndTDto = new TaskManagerInfoArgAndTDto()
                {
                    taskManagerInfoArg = taskManagerInfoArg,
                    tDto = dto,
                    processInfo = processInfo
                };
                if (useThreadPoolFlag)
                {
                    ThreadPool.QueueUserWorkItem(new WaitCallback(checkBusinessAction), taskManagerInfoArgAndTDto);
                }
                else
                {
                    checkBusinessAction(taskManagerInfoArgAndTDto);
                }
            }
            if (useThreadPoolFlag)
            {
                if (Dtos.Count <= 0) threadEndEvent.Set();
                // 等待线程池执行完。
                threadEndEvent.WaitOne();
            }

            stopwatch.Stop();
            Debug.WriteLine("数据检查完成++++++++++++++++++++++++++++++++++++");
            Debug.WriteLine($"用时:{stopwatch.ElapsedMilliseconds}");
        }

        private void checkBusinessAction(object obj)
        {
            var param = obj as TaskManagerInfoArgAndTDto;
            if (param == null)
            {
                return;
            }
            try
            {

                ExcelTask.CheckBusiness(param.tDto, param.taskManagerInfoArg);
            }
            catch (Exception)
            {
            }
            finally
            {

                param.processInfo.SetProcessRow(getNextSetp(param.tDto));
                if (useThreadPoolFlag)
                {
                    lock (checkBusinessAction_checkThreadPoolIsEnd_lockObj)
                    {
                        checkBusinessAction_checkThreadPoolIsEnd();
                    }
                }
            }
        }
        protected void checkBusinessAction_checkThreadPoolIsEnd()
        {
            // 全局变量未同步数据减1
            checkBusiness_totalThreadNumber--;
            if (checkBusiness_totalThreadNumber <= 0)
            {
                threadEndEvent.Set();
            }
        }

        /// <summary>
        /// 初始化之前
        /// </summary>
        protected virtual void AfterInit(TaskManagerInfoArg taskManagerInfoArg)
        {
        }

        /// <summary>
        /// 初始化之后
        /// </summary>
        protected virtual void BeforeInit(TaskManagerInfoArg taskManagerInfoArg)
        {
        }

        protected void CheckMgRow(TaskManagerInfoArg taskManagerInfoArg)
        {
            if (!this.MgExcel.MgSheet.IsHasRow())
            {
                taskManagerInfoArg.AggregateExcelMessage.Add(new ExcelMessage("表格没有可以导入的数据", "", ExcelMessageType.Error, taskManagerInfoArg.FileExcelTaskTypeInfo));
            }

            var processInfo = ((IProcessInfo)taskManagerInfoArg.FileExcelTaskTypeInfo);
            processInfo.SetProcessTotalRow(this.MgExcel.MgSheet.GetTotleRow());
            Parallel.For(this.MgExcel.MgSheet.StartRowIndex, this.MgExcel.MgSheet.MaxRowIndex, rowIndex =>
             {
                 try
                 {
                     var row = MgExcel.MgSheet.GetRow(rowIndex);
                     ExcelTask.CheckMgRow(row, taskManagerInfoArg);
                 }
                 finally
                 {
                     // processInfo.SetProcessRow();
                 }
             });
            //for (int rowIndex = this.MgExcel.MgSheet.StartRowIndex; rowIndex <= this.MgExcel.MgSheet.MaxRowIndex; rowIndex++)
            //{
            //    try
            //    {
            //        var row = MgExcel.MgSheet.GetRow(rowIndex);
            //        ExcelTask.CheckMgRow(row, taskManagerInfoArg);
            //    }
            //    finally
            //    {
            //        // processInfo.SetProcessRow();
            //    }
            //}
        }

        protected TaskManagerInfoArg GetTaskManagerInfoArg(AggregateExcelMessage aggregateExcelMessage)
        {
            return new TaskManagerInfoArg(MgExcel, FileExcelTaskTypeInfo, aggregateExcelMessage);
        }

        /// <summary>
        /// 把Excel数据数据转换为Dto，默认转换规则为一行数据转换为一个Dto，如需多行数据转换为一个Dto请自行重载
        /// </summary>
        /// <returns></returns>
        protected virtual List<TDto> ConvertToDto(TaskManagerInfoArg taskManagerInfoArg)
        {
            List<TDto> result = new List<TDto>();
            var startRowIndex = MgExcel.MgSheet.StartRowIndex;
            var maxRowIndex = MgExcel.MgSheet.MaxRowIndex;
            for (var rowIndex = startRowIndex; rowIndex <= maxRowIndex; rowIndex++)
            {
                var row = MgExcel.MgSheet.GetRow(rowIndex);
                result.Add(row.ToObject<TDto>());
            }

            return result;
        }

        public void WriteErrorToExcel(string folderPath, AggregateExcelMessage aggregateExcelMessage)
        {
            if (string.IsNullOrWhiteSpace((aggregateExcelMessage?.ToString() ?? ""))) throw new Exception("aggregateExcelMessage为空");
            if (string.IsNullOrWhiteSpace((aggregateExcelMessage.ExcelMessages?.ToString() ?? ""))) throw new Exception("aggregateExcelMessage.ExcelMessages为空");
            if ((aggregateExcelMessage?.ExcelMessages?.Count ?? 0) <= 0) throw new Exception("没有错误信息");
            var errorExcelMessages = aggregateExcelMessage.ExcelMessages.Where(x => x.MessageType == ExcelMessageType.Error || x.MessageType == ExcelMessageType.Waring).ToList();
            if ((errorExcelMessages?.Count ?? 0) <= 0) throw new Exception("没有【错误】或【警告】级别的信息");

            var ExcelErrorMessageList = new List<ExcelErrorMessage>();
            foreach (var item in errorExcelMessages)
            {
                ExcelErrorMessageList.Add(new ExcelErrorMessage()
                {
                    RowIndex = item.RowIndex,
                    ColumnIndex = item.ColumnIndex,
                    Message = item.Message,
                    Detailed = item.Detail
                });
            }
            MgExcel.WriteErrorToNewExcel(folderPath, ExcelErrorMessageList);
        }
        #endregion

        #region 私有方案

        private void initMgExcel(TaskManagerInfoArg taskManagerInfoArg)
        {
            MgExcel = ExcelDao.GetExcel(FileExcelTaskTypeInfo.FilePath);
            (taskManagerInfoArg as ITaskManagerInfoArg).SetMgExcel(MgExcel);//调用显式实现接口给MgExcel赋值
        }

        private void initCheck(TaskManagerInfoArg taskManagerInfoArg)
        {
            this.FileExcelTaskTypeInfo = taskManagerInfoArg.FileExcelTaskTypeInfo;

            if (string.IsNullOrEmpty(FileExcelTaskTypeInfo.FilePath))
            {
                throw new MgException("没有设置文件名称");
            }

            if (!File.Exists(FileExcelTaskTypeInfo.FilePath))
            {
                throw new MgException($"文件不存在或已删除，{FileExcelTaskTypeInfo.FilePath}");
            }
        }

        public virtual List<TExcelDto> GetDto<TExcelDto>() where TExcelDto : ExcelDto, new()
        {
            var result = this.Dtos as List<TExcelDto>;

            return result;
        }

        public virtual void Validate(TaskManagerValidatorsArgsCollect taskManagerValidatorsArgsCollect)
        {

        }


        #endregion

        #region
        private class TaskManagerInfoArgAndTDto
        {
            public TaskManagerInfoArg taskManagerInfoArg { get; set; }
            public TDto tDto { get; set; }
            public IProcessInfo processInfo { get; set; }

            public ManualResetEvent manualResetEvent { get; set; }
        }

        #endregion
    }
}
